home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / x11 / xdraw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-04  |  55.2 KB  |  1,919 lines  |  [TEXT/KAHL]

  1. /* Lower-level drawing routines for the X11 interface to Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. /* The routines in this file are for drawing in maps, but only work
  11.    at the Xlib level, and would be the same no matter what higher-level
  12.    toolkit was in use. */
  13.  
  14. /* At the moment, there are three subwindows of a map that use raw Xlib;
  15.    the unit info, the map view itself, and the list of sides. */
  16.  
  17. #include "conq.h"
  18. #include "xconq.h"
  19.  
  20. int tmpdrawlighting;
  21.  
  22. #define bords_to_draw(m) (numbordtypes > 0 && bwid[(m)->vp->power] > 0)
  23.  
  24. #define conns_to_draw(m) (numconntypes > 0 && cwid[(m)->vp->power] > 0)
  25.  
  26. Unit *x_find_unit_or_occ PROTO ((Side *side, Map *map, Unit *unit,
  27.                  int usx, int usy, int usw, int ush,
  28.                  int sx, int sy));
  29. Unit *x_find_unit_at PROTO ((Side *side, Map *map, int x, int y,
  30.                  int sx, int sy));
  31.  
  32. void draw_feature_name PROTO ((Side *side, Map *map, int fid));
  33.  
  34. static void draw_border_line_multiple PROTO ((Side *side, Map *map,
  35.                     Window win, int sx, int sy,
  36.                     int bitmask, int power, int t));
  37. static void draw_connection_line_multiple PROTO ((Side *side, Window win,
  38.                      int sx, int sy, int bitmask,
  39.                      int power, int c));
  40. static void draw_hex_polygon PROTO ((Side *side, Map *map, Window win,
  41.                     GC gc, int sx, int sy,
  42.                     int power, int dogrid));
  43. static void draw_area_background PROTO ((Side *side, Map *map));
  44. static int cell_drawing_info PROTO ((Side *side, int x, int y, int power, int seall,
  45.                      Pixmap *patp, long *colorp));
  46. static void draw_terrain_row PROTO ((Side *side, Map *map,
  47.                      int x0, int y0, int len, int force));
  48. static void draw_elevations PROTO ((Side *side, Map *map,
  49.                     int x0, int y0, int len));
  50. static void draw_units PROTO ((Side *side, Map *map, int x, int y));
  51. static void draw_unit_and_occs PROTO ((Side *side, Map *map, Unit *unit,
  52.                        int sx, int sy, int sw, int sh, int drawoccs));
  53. static void draw_one_unit PROTO ((Side *side, Map *map, Unit *unit));
  54. static void draw_unit_name PROTO ((Side *side, Map *map, Unit *unit,
  55.                    int sx, int sy, int sw, int sh));
  56. static void draw_people PROTO ((Side *side, Map *map, int x, int y));
  57. static void draw_borders PROTO ((Side *side, Map *map, int x, int y, int b));
  58. static void draw_connections PROTO ((Side *side, Map *map, int x, int y,
  59.                      int c));
  60. static void draw_legend PROTO ((Side *side, Map *map, int x, int y));
  61. static void draw_country_border_line PROTO ((Side *side, Window win,
  62.                          int sx, int sy, int dir,
  63.                          int power));
  64. static void draw_legend_text PROTO ((Side *side, Window win,
  65.                      int sx, int sy, int power, char *str,
  66.                      int color, int maskit));
  67. static void draw_feature_boundary PROTO ((Side *side, Map *map,
  68.                       int x, int y, int fid));
  69. static void draw_cursor_icon PROTO ((Side *side, Window win,
  70.                      int sx, int sy, int sw, int sh));
  71. static void draw_info_text PROTO ((Side *side, Map *map, int x, int y,
  72.                    int len, char *buf));
  73.  
  74.  
  75. /* Put the point x, y in the center of the map, or at least as close as possible. */
  76.  
  77. void
  78. recenter(side, map, x, y)
  79. Side *side;
  80. Map *map;
  81. int x, y;
  82. {
  83.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  84.  
  85.     set_view_focus(map->vp, x, y);
  86.     x_center_on_focus(side, map);
  87.     if (map->vp->sx != oldsx || map->vp->sy != oldsy) {
  88.     draw_map(side, map);
  89.     }
  90. }
  91.  
  92. /* Ensure that given location is visible on the front map.
  93.    We (should) also flush the input because
  94.    any input relating to a different screen is probably worthless. */
  95.  
  96. void
  97. put_on_screen(side, map, x, y)
  98. Side *side;
  99. Map *map;
  100. int x, y;
  101. {
  102.  
  103.     /* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/
  104.     if (x == 0 && y == 0)
  105.       return;
  106.     if (!in_middle(side, map, x, y))
  107.       recenter(side, map, x, y);
  108. }
  109.  
  110. /* Decide whether given location is not too close to edge of screen.
  111.    We do this because it's a pain to move units when half the adjacent
  112.    places aren't even visible.  This routine effectively places a lower
  113.    limit of 5x5 for the map window. (I think) */
  114.  
  115. int
  116. in_middle(side, map, x, y)
  117. Side *side;
  118. Map *map;
  119. int x, y;
  120. {
  121.     int sx, sy, insetx1, insety1, insetx2, insety2;
  122.  
  123.     xform(side, map, x, y, &sx, &sy);
  124.     /* Adjust to be the center of the cell, more reasonable if large. */
  125.     sx += map->vp->hw / 2;  sy += map->vp->hh / 2;
  126.     insetx1 = min(map->vp->pxw / 4, 1 * map->vp->hw);
  127.     insety1 = min(map->vp->pxh / 4, 1 * map->vp->hch);
  128.     insetx2 = min(map->vp->pxw / 4, 2 * map->vp->hw);
  129.     insety2 = min(map->vp->pxh / 4, 2 * map->vp->hch);
  130.     if (sx < insetx2)
  131.       return FALSE;
  132.     if (sx > map->vp->pxw - insetx2)
  133.       return FALSE;
  134.     if (sy < (between(2, y, area.height-3) ? insety2 : insety1))
  135.       return FALSE;
  136.     if (sy > map->vp->pxh - (between(2, y, area.height-3) ? insety2 : insety1))
  137.       return FALSE;
  138.     return TRUE;
  139. }
  140.  
  141. /* Transform map coordinates into screen coordinates, relative to the given
  142.    side.  Allow for cylindricalness and number of pixels in a cell. */
  143.  
  144. int
  145. xform(side, map, x, y, sxp, syp)
  146. Side *side;
  147. Map *map;
  148. int x, y, *sxp, *syp;
  149. {
  150.     xform_cell(map->vp, x, y, sxp, syp);
  151.     return TRUE;
  152. }
  153.  
  154. int
  155. x_xform_unit(side, map, unit, sxp, syp, swp, shp)
  156. Side *side;
  157. Map *map;
  158. Unit *unit;
  159. int *sxp, *syp, *swp, *shp;
  160. {
  161.     xform_unit(map->vp, unit, sxp, syp, swp, shp);
  162.     return TRUE;
  163. }
  164.  
  165. int
  166. x_xform_unit_self(side, map, unit, sxp, syp, swp, shp)
  167. Side *side;
  168. Map *map;
  169. Unit *unit;
  170. int *sxp, *syp, *swp, *shp;
  171. {
  172.     xform_unit_self(map->vp, unit, sxp, syp, swp, shp);
  173.     return TRUE;
  174. }
  175.  
  176. int
  177. x_xform_occupant(side, map, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp)
  178. Side *side;
  179. Map *map;
  180. Unit *transport, *unit;
  181. int sx, sy, sw, sh, *sxp, *syp, *swp, *shp;
  182. {
  183.     xform_occupant(map->vp, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp);
  184.     return TRUE;
  185. }
  186.  
  187. int
  188. x_nearest_cell(side, map, sx, sy, xp, yp)
  189. Side *side;
  190. Map *map;
  191. int sx, sy, *xp, *yp;
  192. {
  193.     return nearest_cell(map->vp, sx, sy, xp, yp);
  194. }
  195.  
  196. /* Find the closest direction of the closest boundary. */
  197.  
  198. int
  199. x_nearest_boundary(side, map, sx, sy, xp, yp, dirp)
  200. Side *side;
  201. Map *map;
  202. int sx, sy, *xp, *yp, *dirp;
  203. {
  204.     return nearest_boundary(map->vp, sx, sy, xp, yp, dirp);
  205. }
  206.  
  207. Unit *
  208. x_find_unit_or_occ(side, map, unit, usx, usy, usw, ush, sx, sy)
  209. Side *side;
  210. Map *map;
  211. Unit *unit;
  212. int usx, usy, usw, ush, sx, sy;
  213. {
  214.     int usx1, usy1, usw1, ush1;
  215.     Unit *occ, *rslt;
  216.  
  217.     /* See if the point might be over an occupant. */
  218.     if (unit->occupant != NULL) {
  219.     for_all_occupants(unit, occ) {
  220.         x_xform_unit(side, map, occ, &usx1, &usy1, &usw1, &ush1);
  221.         rslt =
  222.           x_find_unit_or_occ(side, map, occ, usx1, usy1, usw1, ush1, sx, sy);
  223.         if (rslt) {
  224.         return rslt;
  225.         }
  226.     }
  227.     }
  228.     /* Otherwise see if it could be the unit itself.  This has the effect of
  229.        "giving" the transport everything in its box that is not in an occ. */
  230.     x_xform_unit(side, map, unit, &usx1, &usy1, &usw1, &ush1);
  231.     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1)) {
  232.     return unit;
  233.     }
  234.     return NULL;
  235. }
  236.  
  237. Unit *
  238. x_find_unit_at(side, map, x, y, sx, sy)
  239. Side *side;
  240. Map *map;
  241. int x, y, sx, sy;
  242. {
  243.     int usx, usy, usw, ush;
  244.     Unit *unit, *rslt;
  245.     
  246.     for_all_stack(x, y, unit) {
  247.     x_xform_unit(side, map, unit, &usx, &usy, &usw, &ush);
  248.     rslt = x_find_unit_or_occ(side, map, unit, usx, usy, usw, ush, sx, sy);
  249.     if (rslt)
  250.       return rslt;
  251.     }
  252.     return NULL;
  253. }
  254.  
  255. int
  256. x_nearest_unit(side, map, sx, sy, unitp)
  257. Side *side;
  258. Map *map;
  259. int sx, sy;
  260. Unit **unitp;
  261. {
  262.     int x, y;
  263.  
  264.     if (!x_nearest_cell(side, map, sx, sy, &x, &y)) {
  265.     *unitp = NULL;
  266.     } else if (map->vp->power > 4) {
  267.     *unitp = x_find_unit_at(side, map, x, y, sx, sy);
  268.     } else {
  269.     *unitp = unit_at(x, y);
  270.     }
  271.     DGprintf("Pixel %d,%d -> unit %s\n", sx, sy, unit_desig(*unitp));
  272.     return TRUE;
  273. }
  274.  
  275. /* Draw all the maps that are currently up. */
  276.  
  277. void
  278. draw_all_maps(side)
  279. Side *side;
  280. {
  281.     Map *map;
  282.  
  283.     for_all_maps(side, map) {
  284.     draw_map(side, map);
  285.     }
  286. }
  287.  
  288. /* Display a map and all of its paraphernalia. */
  289.  
  290. void
  291. draw_map(side, map)
  292. Side *side;
  293. Map *map;
  294. {
  295.     /* Redraw only the panes that are managed manually. */
  296.     draw_map_sides(side, map);
  297.     draw_map_info(side, map);
  298.     draw_map_view(side, map);
  299.     flush_output(side);
  300. }
  301.  
  302. /* Draw the background area for the map. */
  303.  
  304. static void
  305. draw_area_background(side, map)
  306. Side *side;
  307. Map *map;
  308. {
  309.     int sx, sy, aw, ah, i;
  310.     int llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
  311.     XPoint points[7];
  312.     Display *dpy = side->ui->dpy;
  313.     GC gc = side->ui->terrgc;
  314.  
  315.     XSetClipMask(dpy, gc, None);
  316.     XSetFillStyle(dpy, gc, FillSolid);
  317.     XSetForeground(dpy, gc, side->ui->fgcolor);
  318.     XFillRectangle(dpy, map->viewwin, gc,
  319.            0, 0, map->vp->pxw, map->vp->pxh);
  320.     if (1 /* grid color matches unseen color */) {
  321.     XSetForeground(dpy, gc, side->ui->gridcolor);
  322.     }
  323.     XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
  324.  
  325.     if (area.xwrap) {
  326.     /* Area is cylinder; draw a rectangle. */
  327.     xform(side, map, 0, 0, &sx, &sy);
  328.     XFillRectangle(dpy, map->viewwin, gc,
  329.                0, 0, area.width * map->vp->hw, sy);
  330.     XSetForeground(dpy, gc, side->ui->whitecolor);
  331.     XDrawRectangle(dpy, map->viewwin, gc,
  332.                0, 0, area.width * map->vp->hw, sy);
  333.     } else {
  334.     /* Area is hexagon; draw a hexagon. */
  335.     aw = area.width;  ah = area.height;
  336.     xform(side, map, 0 + ah / 2, 0, &llx, &lly);
  337.     points[0].x = llx;  points[0].y = lly;
  338.     xform(side, map, aw - 1, 0, &lrx, &lry);
  339.     points[1].x = lrx;  points[1].y = lry;
  340.     xform(side, map, aw - 1, ah / 2, &rx, &ry);
  341.     points[2].x = rx;   points[2].y = ry;
  342.     xform(side, map, aw - 1 - ah / 2, ah - 1, &urx, &ury);
  343.     points[3].x = urx;  points[3].y = ury;
  344.     xform(side, map, 0, ah - 1, &ulx, &uly);
  345.     points[4].x = ulx;  points[4].y = uly;
  346.     xform(side, map, 0, ah / 2, &lx, &ly);
  347.     points[5].x = lx;   points[5].y = ly;
  348.     /* Offset so polygon edges run through middles of cells. */
  349.     for (i = 0; i < 6; ++i) {
  350.         points[i].x += map->vp->hw / 2;  points[i].y += map->vp->hh / 2;
  351.     }
  352.     /* End up where we started. */
  353.     points[6].x = points[0].x;  points[6].y = points[0].y;
  354.     XFillPolygon(dpy, map->viewwin, gc,
  355.              points, 6, Convex, CoordModeOrigin);
  356.     XSetForeground(dpy, gc, side->ui->whitecolor);
  357.     XDrawLines(dpy, map->viewwin, gc, points, 7, CoordModeOrigin);
  358.     }
  359.     /* This probably drew a large area; make sure it's all out there. */
  360.     XFlush(dpy);
  361.     /* Restore the foreground color. */
  362.     XSetForeground(dpy, gc, side->ui->fgcolor);
  363. }
  364.  
  365. /* Draw the view proper. */
  366.  
  367. void
  368. draw_map_view(side, map)
  369. Side *side;
  370. Map *map;
  371. {
  372.     int y1, y2, y, x1, x2, adj;
  373.     int halfheight = area.height / 2;
  374.  
  375.     draw_area_background(side, map);
  376.  
  377.     if (map->vp->vcx < 0 || map->vp->vcy < 0) {
  378.         run_warning("doing a nasty hack");
  379.     map->vp->vcx = map->vp->vcy = 2;
  380.     }
  381.     /* Compute the width and height. */
  382.     map->vw = min(area.width, map->pxw / map->vp->hw + 2);
  383.     map->vh = min(area.height, map->pxh / map->vp->hch + 2);
  384.     /* Compute some cached values that are used a lot (still?). */
  385.     map->vw2 = map->vw / 2;  map->vh2 = map->vh / 2;
  386.  
  387.     map->vy = ((map->vp->totsh - map->vp->sy) / map->vp->hch) - map->vh;
  388.     /* Now adjust the bottom row so it doesn't go outside the area. */
  389.     if (map->vy < 0)
  390.       map->vy = 0;
  391.     if (map->vy > area.height - map->vh)
  392.       map->vy = area.height - map->vh;
  393.     /* Compute the leftmost "column". */
  394.     map->vx = map->vp->sx / map->vp->hw - map->vy / 2 - 1;
  395.     DGprintf("Set map %x viewport to be %dx%d @ %d,%d (nom center %d,%d)\n",
  396.          map, map->vw, map->vh, map->vx, map->vy,
  397.          map->vp->vcx, map->vp->vcy);
  398.     DGprintf("Displaying map view at %d,%d, size %d,%d, center %d,%d\n",
  399.          map->vx, map->vy, map->vw, map->vh, map->vp->vcx, map->vp->vcy);
  400.     /* Compute top and bottom rows to be displayed. */
  401.     /* Include rows that will only be partly drawn. */
  402.     y1 = min(map->vy + map->vh, area.height - 1);
  403.     y2 = map->vy;
  404.     for (y = y1; y >= y2; --y) {
  405.     /* Adjust the right and left bounds to fill the viewport as
  406.        much as possible, without going too far (the drawing code
  407.        will clip, but clipped drawing is still expensive). */
  408.     /* could test by drawing viewport rect as lines... */
  409.     adj = (y - map->vy) / 2;
  410.     /* If the area doesn't wrap, then we might have to stop
  411.        drawing before we reach the edge of the viewport. */
  412.     /* (is this really reliable?) */
  413.     x1 = map->vx - (y - map->vy) / 2;
  414.     x2 = x1 + map->vw + 2 /* bleah, shouldn't be necessary */;
  415.     if (area.xwrap) {
  416.     } else {
  417.         /* Truncate x's to stay within the area. */
  418.         x1 = max(0, min(x1, area.width-1));
  419.         x2 = max(0, min(x2, area.width));
  420.         /* If this row is entirely in the NE corner, don't draw anything. */
  421.         if (x1 + y > area.width + halfheight)
  422.           continue;
  423.         /* If this row is entirely in the SW corner, don't draw anything. */
  424.         if (x2 + y < halfheight)
  425.           continue;
  426.         /* If the row ends up in the NE corner, shorten it. */
  427.         if (x2 + y > area.width + halfheight)
  428.           x2 = area.width + halfheight - y;
  429.         /* If the row starts out in the SW corner, shorten it. */
  430.         if (x1 + y < halfheight)
  431.           x1 = halfheight - y;
  432.     }
  433.     draw_row(side, map, x1, y, x2 - x1, FALSE);
  434.     /* (should test for input events here, would respond better) */
  435.     }
  436.     draw_current(side, map);
  437. }
  438.  
  439. /* The basic map drawing routine does an entire row at a time, which yields
  440.    order-of-magnitude speedups. */
  441.  
  442. void
  443. draw_row(side, map, x0, y0, len, clearit)
  444. Side *side;
  445. Map *map;
  446. int x0, y0, len, clearit;
  447. {
  448.     int x, b, c, i;
  449.  
  450.     if (map->drawterrain) {
  451.     draw_terrain_row(side, map, x0, y0, len, (clearit == -1));
  452.     /* The relative ordering of these is quite important.  Note that
  453.        each should be prepared to run independently also, since the
  454.        other displays might have been turned off. */
  455.     if (any_aux_terrain_defined()) {
  456.         if (bords_to_draw(map)) {
  457.         for_all_terrain_types(b) {
  458.             if (t_is_border(b) && aux_terrain_defined(b)) {
  459.             for (x = x0; x < x0 + len; ++x) {
  460.                 draw_borders(side, map, x, y0, b);
  461.             }
  462.             }
  463.         }
  464.         }
  465.         /* Draw the connections on top of the borders. */
  466.         if (conns_to_draw(map)) {
  467.         for_all_terrain_types(c) {
  468.             if (t_is_connection(c) && aux_terrain_defined(c)) {
  469.             for (x = x0; x < x0 + len; ++x) {
  470.                 draw_connections(side, map, x, y0, c);
  471.             }
  472.             }
  473.         }
  474.         }
  475.     }
  476.     if (features_defined() && map->drawfeatureboundaries) {
  477.         for (x = x0; x < x0 + len; ++x) {
  478.         if (!inside_area(x, y0))
  479.           continue;
  480.         draw_feature_boundary(side, map, x, y0, raw_feature_at(x, y0));
  481.         }
  482.     }
  483.     }
  484.     if (features_defined() && map->drawfeaturenames && side->ui->legends) {
  485.     for (i = 0; i < side->ui->numfeatures; ++i) {
  486.         if (side->ui->legends[i].oy == y0) {
  487.         draw_feature_name(side, map, i);
  488.         }
  489.     }
  490.     }
  491.     if (elevations_defined() && map->drawelevations && map->vp->hw >= 20) {
  492.     draw_elevations(side, map, x0, y0, len);
  493.     }
  494.     /* Draw sparse things on top of the basic row. */
  495.     if (people_sides_defined() && map->drawpeople && map->vp->hw >= 8) {
  496.     for (x = x0; x < x0 + len; ++x) {
  497.         if (!inside_area(x, y0))
  498.           continue;
  499.         draw_people(side, map, x, y0);
  500.     }
  501.     }
  502.     /* Draw units. */
  503.     if (map->drawunits) {
  504.     for (x = x0; x < x0 + len; ++x) {
  505.         if (!inside_area(x, y0))
  506.           continue;
  507.         draw_units(side, map, x, y0);
  508.     }
  509.     }
  510.     if (map->drawnames && map->vp->hh >= 8) {
  511.     for (x = x0; x < x0 + len; ++x) {
  512.         if (!inside_area(x, y0))
  513.           continue;
  514.         draw_legend(side, map, x, y0);
  515.     }
  516.     }
  517. }
  518.  
  519. char buffer[BUFSIZE];
  520.  
  521. void
  522. draw_feature_name(side, map, f)
  523. Side *side;
  524. Map *map;
  525. int f;
  526. {
  527.     Legend *leg = &side->ui->legends[f];
  528.     int y = leg->oy;
  529.     int x = leg->ox;
  530.     int d = ((leg->dx+1) * map->vp->hw * 9)/10;
  531.     int i, b, l, lnew, sx0, sy0, sxc2, syc;
  532.     char *pad, *name;
  533.     XCharStruct *bounds;
  534.  
  535.     if (leg->dist < 0 || y < map->vy || y > map->vy + map->vh)
  536.       return;
  537.  
  538.     name = feature_desc(find_feature(f+1), buffer);
  539.     if (!name || !name[0])
  540.       return;
  541.     
  542.     XSetFillStyle(side->ui->dpy, side->ui->gc, FillSolid);
  543.     /* we should choose a color contrasting with main terrain */
  544.     XSetForeground(side->ui->dpy, side->ui->gc, side->ui->fgcolor);
  545.     xform(side, map, x, y, &sx0, &sy0);
  546.     /* xform returns coordinates of the upper-left corner of the cell */
  547.     sxc2 = 2 * sx0 + (leg->dx + 1) * map->vp->hw; /* twice center x */
  548.     syc  = sy0 + map->vp->hh / 2;          /* center y */
  549.     if (sxc2 + 2 * d < 0)
  550.       return;
  551.  
  552.     l = XTextWidth(side->ui->flegendfonts[0], name, strlen(name));
  553.     for (i = 1; i < 5; i++) {
  554.     /* check if the font is too tall for the current magnification */
  555.     bounds = &side->ui->flegendfonts[i]->max_bounds;
  556.     if (bounds->ascent+bounds->descent > map->vp->hch)
  557.       break;
  558.     lnew = XTextWidth(side->ui->flegendfonts[i], name, strlen(name));
  559.     if (lnew > d) {
  560.         XSetFont(side->ui->dpy, side->ui->gc, side->ui->flegendfids[i-1]);
  561.         bounds = &side->ui->flegendfonts[i-1]->max_bounds;
  562.         XDrawString(side->ui->dpy, map->viewwin, side->ui->gc,
  563.             (sxc2 - l) / 2, syc + (bounds->ascent - bounds->descent) / 2,
  564.             name, strlen(name));
  565.         return;
  566.     }
  567.     l = lnew;
  568.     }
  569.     /* retain the biggest usable font */
  570.     if (i)
  571.       --i;
  572.  
  573.     XSetFont(side->ui->dpy, side->ui->gc, side->ui->flegendfids[i]);
  574.     bounds = &side->ui->flegendfonts[i]->max_bounds;
  575.     for (b = 1; b < 21; b++) {
  576.     pad = pad_blanks(name, b);
  577.     lnew = XTextWidth(side->ui->flegendfonts[i], pad, strlen(pad));
  578.     if (lnew > d || b == 20) {
  579.         if (b == 20) {
  580.         l = lnew;
  581.         b++;
  582.         }
  583.         pad = pad_blanks(name, b - 1);
  584.          /* map->pxw is the window width */
  585.          if (sxc2 - l > 2 * map->pxw)
  586.           return; 
  587.         XDrawString(side->ui->dpy, map->viewwin, side->ui->gc,
  588.             (sxc2 - l) / 2,
  589.             syc + (bounds->ascent - bounds->descent) / 2,
  590.             pad, strlen(pad));
  591.         return;
  592.     }
  593.     l = lnew;
  594.     }
  595. }
  596.  
  597. static int
  598. cell_drawing_info(side, x, y, power, seeall, patp, colorp)
  599. Side *side;
  600. int x, y, power, seeall;
  601. Pixmap *patp;
  602. long *colorp;
  603. {
  604.     int t;
  605.     enum whattouse rslt;
  606.  
  607.     *patp = None;
  608.     *colorp = side->ui->whitecolor;
  609.     if (seeall || terrain_visible(side, x, y)) {
  610.     t = terrain_at(x, y);
  611.     *patp = side->ui->terrpics[power][t];
  612.     *colorp = side->ui->cellcolor[t];
  613.     rslt = side->ui->usewhat[power][t];
  614.     } else {
  615.     rslt = dontdraw;
  616.     }
  617.     return rslt;
  618. }
  619.  
  620. static void set_terrain_gc_for_image PROTO ((Side *side, Image *timg));
  621.  
  622. static void
  623. set_terrain_gc_for_image(side, timg)
  624. Side *side;
  625. Image *timg;
  626. {
  627.     X11Image *ximg;
  628.     Display *dpy = side->ui->dpy;
  629.     GC gc = side->ui->terrgc;
  630.  
  631.     if (timg != NULL) {
  632.     ximg = (X11Image *) timg->hook;
  633.     if (ximg != NULL) {
  634.         if (!side->ui->monochrome) {
  635.         if (side->ui->dflt_color_terr_images) {
  636.             if (ximg->colr != None) {
  637.             XSetFillStyle(dpy, gc, FillTiled);
  638.             XSetTile(dpy, gc, ximg->colr);
  639.             } else if (ximg->mono != None) {
  640.             XSetFillStyle(dpy, gc, FillOpaqueStippled);
  641.             XSetStipple(dpy, gc, ximg->mono);
  642.             } else {
  643.             XSetFillStyle(dpy, gc, FillSolid);
  644.             }
  645.         } else {
  646.             XSetFillStyle(dpy, gc, FillSolid);
  647.         }
  648.         } else if (ximg->mono != None) {
  649.         XSetFillStyle(dpy, gc, FillOpaqueStippled);
  650.         XSetStipple(dpy, gc, ximg->mono);
  651.         } else {
  652.         /* No pattern, no color - what to do? */
  653.         XSetFillStyle(dpy, gc, FillSolid);
  654.         }
  655.     } else {
  656.         XSetFillStyle(dpy, gc, FillSolid);
  657.     }
  658.     } else {
  659.     XSetFillStyle(dpy, gc, FillSolid);
  660.     }
  661. }
  662.  
  663. /* This interfaces higher-level drawing decisions to the rendition of
  664.    individual pieces of display.  The rendering technique chosen depends
  665.    on what the init code has decided is appropriate given what it found
  666.    during init and what magnification the display is at.
  667.  
  668.    This routine is performance-critical;  any improvements will probably
  669.    have a noticeable effect on the display.  But also note that X's
  670.    main bottleneck is the network connection, so it's more useful to
  671.    eliminate roundtrips to the server than anything else. */
  672.  
  673. /* (should cache best images, never have to look up in here) */
  674.  
  675. static char *cellbuf = NULL;
  676.  
  677. static void
  678. draw_terrain_row(side, map, x0, y0, len, force)
  679. Side *side;
  680. Map *map;
  681. int x0, y0, len, force;
  682. {
  683.     int x1, x0w, x, xw, t, sx, sy, i = 0, j;
  684.     int w = map->vp->hw, h = map->vp->hh, p = map->vp->power;
  685.     int seeall = map->seeall;
  686.     int dogrid = map->drawgrid, dofill = map->drawcellpats;
  687.     long color, segcolor;
  688.     enum whattouse drawmethod, segdrawmethod;
  689.     Pixmap pat, segpat;
  690.     Image *timg;
  691.     Display *dpy = side->ui->dpy;
  692.     GC gc = side->ui->terrgc;
  693.  
  694.     x1 = x0;
  695.     x0w = wrapx(x0);
  696.     segdrawmethod = cell_drawing_info(side, x0w, y0, p, seeall, &segpat, &segcolor);
  697.     for (x = x0; x < x0 + len + 1; ++x) {
  698.     xw = wrapx(x);
  699.     t = terrain_at(xw, y0);
  700.     drawmethod = cell_drawing_info(side, xw, y0, p, seeall, &pat, &color);
  701.     /* Decide if the run is over and we need to dump some output. */
  702.     if (x == x0 + len
  703.         || drawmethod != segdrawmethod
  704.         || color != segcolor
  705.         || pat != segpat
  706.         || segdrawmethod == usepolygons
  707.         || force) {
  708.         /* Note: we might end up drawing something that matches
  709.            the background color, which wastes time, but apparently
  710.            the test "(segdrawmethod != dontdraw && segcolor !=
  711.            side->ui->bgcolor)" is not completely sufficient.
  712.            (should figure this one out sometime) */
  713.         t = terrain_at(wrapx(x1), y0);
  714.         timg = best_image(side->ui->timages[t], w, h);
  715.         xform(side, map, x1, y0, &sx, &sy);
  716.         /* Last-minute fixup for when we're erasing a cell. */
  717.         if (force && segdrawmethod == dontdraw) {
  718.         segdrawmethod = side->ui->usewhat[p][t];
  719.         /* this must match the bg set in draw_area_background */
  720.         segcolor = side->ui->gridcolor /* bgcolor */;
  721.         timg = NULL;
  722.         }
  723.         XSetForeground(dpy, gc, segcolor);
  724.         XSetBackground(dpy, gc, side->ui->whitecolor);
  725.         switch (segdrawmethod) {
  726.           case dontdraw:
  727.             /* Don't do anything. */
  728.             break;
  729.           case useblocks:
  730.         /* maybe adjust to go along middle of cell borders? */
  731.         XSetClipMask(dpy, gc, None);
  732.         set_terrain_gc_for_image(side, timg);
  733.         XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
  734.         break;
  735.           case usepictures:
  736.         if (dofill && 0 /* have individual pictures */) {
  737.             XSetClipMask(dpy, gc, segpat);
  738.         } else if (dogrid) {
  739.             XSetClipMask(dpy, gc, side->ui->bhexpics[p]);
  740.         } else {
  741.             XSetClipMask(dpy, gc, side->ui->hexpics[p]);
  742.         }
  743.         set_terrain_gc_for_image(side, timg);
  744.         for (j = 0; j < i; ++j) {
  745.             xform(side, map, x1 + j, y0, &sx, &sy);
  746.             XSetClipOrigin(dpy, gc, sx, sy);
  747.             XFillRectangle(dpy, map->viewwin, gc, sx, sy, w, h);
  748.         }
  749.         break;
  750.           case usefontchars:
  751.         if (cellbuf == NULL)
  752.           cellbuf = xmalloc(area.width + 1);
  753.         if (dofill) {
  754.             cellbuf[i] = side->ui->terrchars[p][t];
  755.         } else if (dogrid) {
  756.             cellbuf[i] = 'o';
  757.         } else {
  758.             cellbuf[i] = 'O';
  759.         }
  760.         /* No clipping when we're drawing cells with text. */
  761.         XSetClipMask(dpy, gc, None);
  762.         XDrawString(dpy, map->viewwin, gc, sx, sy, cellbuf, i);
  763.         break;
  764.           case usepolygons:
  765.         /* No clipping when we're drawing big polygons. */
  766.         XSetClipMask(dpy, gc, None);
  767.         set_terrain_gc_for_image(side, timg);
  768.         draw_hex_polygon(side, map, map->viewwin, gc, sx, sy, p, dogrid);
  769.         }
  770.         /* Reset everything for the next run. */
  771.         i = 0;
  772.         x1 = x;
  773.         segdrawmethod = drawmethod;
  774.         segpat = pat;
  775.         segcolor = color;
  776.     }
  777.     ++i;
  778.     }
  779. }
  780.  
  781. static void
  782. draw_elevations(side, map, x0, y0, len)
  783. Side *side;
  784. Map *map;
  785. int x0, y0, len;
  786. {
  787.     int x, xw, sx, sy;
  788.  
  789.     if (map->vp->hw < 20)
  790.       return;
  791.     for (x = x0; x < x0 + len + 1; ++x) {
  792.     xw = wrapx(x);
  793.     if (terrain_visible(side, xw, y0)) {
  794.         sprintf(spbuf, "%d", elev_at(xw, y0));
  795.         xform(side, map, x, y0, &sx, &sy);
  796.         draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
  797.               spbuf, side->ui->fgcolor);
  798.     }
  799.     }
  800. }
  801.  
  802. /* Draw a single unit icon as appropriate.  This *also* has a bunch of
  803.    details to worry about: centering of icon in cell, clearing a rectangular
  804.    area for the icon, picking a color for the unit, using either a bitmap
  805.    or font char, and adding a side emblem.
  806.  
  807.    Must also be careful not to draw black-on-black for units in space. */
  808.  
  809. static void
  810. draw_units(side, map, x, y)
  811. Side *side;
  812. Map *map;
  813. int x, y;
  814. {
  815.     int sx, sy, sw, sh, uview, u, s;
  816.     int uw = map->vp->uw, uh = map->vp->uh;
  817.     Unit *unit;
  818.     Display *dpy = side->ui->dpy;
  819.     GC gc = side->ui->unitgc;
  820.  
  821.     if (units_visible(side, x, y) && unit_at(x, y) != NULL) {
  822.     if (uw <= 16) {
  823.         unit = unit_at(x, y);
  824.         xform(side, map, x, y, &sx, &sy);
  825.         /* Adjust to unit part of cell. */
  826.         sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
  827.         if (unit != NULL) {
  828.         if (unit->occupant != NULL
  829.                 && (side_controls_unit(side, unit)
  830.             || map->seeall
  831.             || u_see_occupants(unit->type))) {
  832.             /* Draw a "grouping box", in white, but with no occs
  833.                actually drawn. */
  834.             XSetClipMask(dpy, gc, None);
  835.             XSetForeground(dpy, gc, side->ui->whitecolor);
  836.             XFillRectangle(dpy, map->viewwin, gc,
  837.                           sx + 1, sy + 1, uw - 2, uh - 2);
  838.             /* Put a black border around it, for better contrast. */
  839.             XSetForeground(dpy, gc, side->ui->blackcolor);
  840.             XDrawRectangle(dpy, map->viewwin, gc,
  841.                    sx + 1, sy + 1, uw - 2, uh - 2);
  842.             }
  843.         draw_unit_image(side, map->viewwin, sx, sy, uw, uh,
  844.                 unit->type, side_number(unit->side),
  845.                 -1, -1);
  846.         /* (should indicate presence of other units, a la Mac) */
  847.         if (map->drawnames)
  848.           draw_unit_name(side, map, unit, sx, sy, uw, uh);
  849.         }
  850.     } else {
  851.         for_all_stack(x, y, unit) {
  852.         x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
  853.         draw_unit_and_occs(side, map, unit, sx, sy, sw, sh, TRUE);
  854.         }
  855.     }
  856.     } else if ((uview = unit_view(side, x, y)) != EMPTY) {
  857.     u = vtype(uview);  s = vside(uview);
  858.     if (s == side_number(side))
  859.       return; /* should make error */
  860.     xform(side, map, x, y, &sx, &sy);
  861.     sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
  862.     draw_unit_image(side, map->viewwin, sx, sy, uw, uh,
  863.             u, s, -1, -1);
  864.     }
  865. }
  866.  
  867. static void
  868. draw_unit_and_occs(side, map, unit, sx, sy, sw, sh, drawoccs)
  869. Side *side;
  870. Map *map;
  871. Unit *unit;
  872. int sx, sy, sw, sh, drawoccs;
  873. {
  874.     int u = unit->type, s = side_number(unit->side), sx2, sy2, sw2, sh2;
  875.     Unit *occ;
  876.     Display *dpy = side->ui->dpy;
  877.     GC gc = side->ui->unitgc;
  878.  
  879.     /* If an occupant's side is the same as its transport's, then there's
  880.        really no need to draw its side emblem, since the transport's emblem
  881.        will also be visible. */
  882.     if (unit->transport && unit->side == unit->transport->side)
  883.       s = -1;
  884.     if (unit->occupant == NULL) {
  885.     draw_unit_image(side, map->viewwin, sx, sy, sw, sh, u, s,
  886.             -1, -1);
  887.     if (map->drawnames)
  888.       draw_unit_name(side, map, unit, sx, sy, sw, sh);
  889.     } else {
  890.     x_xform_occupant(side, map, unit, unit, sx, sy, sw, sh,
  891.                &sx2, &sy2, &sw2, &sh2);
  892.     /* Draw a white box to indicate the grouping. */
  893.     XSetClipMask(dpy, gc, None);
  894.     XSetForeground(dpy, gc, side->ui->whitecolor);
  895.     XFillRectangle(dpy, map->viewwin, gc,
  896.                sx+1, sy+1, sw-2, sh-2);
  897.     /* Put a black border around it, for better contrast. */
  898.     XSetForeground(dpy, gc, side->ui->blackcolor);
  899.     XDrawRectangle(dpy, map->viewwin, gc,
  900.                sx+1, sy+1, sw-2, sh-2);
  901.     /* Draw the transport's image. */
  902.     draw_unit_image(side, map->viewwin, sx2, sy2, sw2, sh2, u, s,
  903.             -1, -1);
  904.     if (map->drawnames)
  905.       draw_unit_name(side, map, unit, sx2, sy2, sw2, sh2); 
  906.     /* Maybe draw all of its occupants, recursively. */
  907.     if (drawoccs) {
  908.         for_all_occupants(unit, occ) {
  909.         x_xform_occupant(side, map, unit, occ, sx, sy, sw, sh,
  910.                  &sx2, &sy2, &sw2, &sh2);
  911.         draw_unit_and_occs(side, map, occ, sx2, sy2, sw2, sh2, TRUE);
  912.         }
  913.     }
  914.     }
  915. }
  916.  
  917. static void
  918. draw_one_unit(side, map, unit)
  919. Side *side;
  920. Map *map;
  921. Unit *unit;
  922. {
  923.     int sx, sy, sw, sh;
  924.     int uw = map->vp->uw, uh = map->vp->uh;
  925.     Display *dpy = side->ui->dpy;
  926.     GC gc = side->ui->unitgc;
  927.  
  928.     if (unit == NULL || !in_area(unit->x, unit->y))
  929.       return;
  930.     if (map->vp->uw <= 16) {
  931.     xform(side, map, unit->x, unit->y, &sx, &sy);
  932.     /* Adjust to unit part of cell. */
  933.     sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
  934.     if (unit == unit_at(unit->x, unit->y)) {
  935.         if (unit->occupant != NULL
  936.         && (side_controls_unit(side, unit)
  937.             || map->seeall
  938.             || u_see_occupants(unit->type))) {
  939.         /* Draw a "grouping box", in white, but with no occs drawn. */
  940.         XSetForeground(dpy, gc, side->ui->whitecolor);
  941.         XFillRectangle(dpy, map->viewwin, gc,
  942.                    sx + 1, sy + 1, uw - 2, uh - 2);
  943.         XSetForeground(dpy, gc, side->ui->blackcolor);
  944.         XFillRectangle(dpy, map->viewwin, gc,
  945.                    sx + 1, sy + 1, uw - 2, uh - 2);
  946.         }
  947.         draw_unit_image(side, map->viewwin, sx, sy, uw, uh,
  948.                 unit->type, side_number(unit->side),
  949.                 -1, -1);
  950.         /* (should indicate presence of other units, a la Mac) */
  951.         if (map->drawnames)
  952.           draw_unit_name(side, map, unit, sx, sy, uw, uh);
  953.     }
  954.     } else {
  955.     x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
  956.     draw_unit_and_occs(side, map, unit, sx, sy, sw, sh, FALSE);
  957.     }
  958. }
  959.  
  960. static void
  961. draw_unit_name(side, map, unit, sx, sy, sw, sh)
  962. Side *side;
  963. Map *map;
  964. Unit *unit;
  965. int sx, sy, sw, sh;
  966. {
  967.     char legend[BUFSIZE];
  968.  
  969.     name_or_number(unit, legend);
  970.     if (strlen(legend)) {
  971.     draw_legend_text(side, map->viewwin,
  972.              sx + sw,
  973.              sy + sh / 2 - 5,
  974.              map->vp->power, legend, side->ui->fgcolor, TRUE);
  975.     }
  976. }
  977.  
  978.  
  979. /* Indicate what kind of people are living in the given cell. */
  980.  
  981. static void
  982. draw_people(side, map, x, y)
  983. Side *side;
  984. Map *map;
  985. int x, y;
  986. {
  987.     int pop, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
  988.     int bordercell = FALSE;
  989.     Side *side2;
  990.     
  991.     if (terrain_visible(side, x, y)) {
  992.     pop = people_side_at(wrapx(x), y);
  993.     side2 = side_n(pop);
  994.     xform(side, map, x, y, &sx, &sy);
  995.     /* Decide which edges are borders of the country. */
  996.     for_all_directions(dir) {
  997.         if (point_in_dir(x, y, dir, &x1, &y1)) {
  998.         pop1 = people_side_at(wrapx(x1), y1);
  999.         if (pop != pop1) {
  1000.             if (pop1 != NOBODY) {
  1001.             draw_country_border_line(side, map->viewwin,
  1002.                          sx, sy, dir, map->vp->power);
  1003.             } else {
  1004.             /* should draw in gray instead? */
  1005.             draw_country_border_line(side, map->viewwin,
  1006.                          sx, sy, dir, map->vp->power);
  1007.             }
  1008.             bordercell = TRUE;
  1009.         }
  1010.         }
  1011.     }
  1012.     /* Draw an emblem for the people in the cell. */
  1013.     if (map->drawpeople && bordercell) {
  1014.         sw = map->vp->hw;  sh = map->vp->hh;
  1015.         ew = min(sw, max(8, sw / 4));  eh = min(sh, max(8, sh / 4));
  1016.         ex = sx + sw / 2 - ew / 2;  ey = sy + sh / 2 - eh / 2;
  1017.         draw_side_emblem(side, map->viewwin, ex, ey, ew, eh,
  1018.                  side_number(side2), 0);
  1019.     }
  1020.     }
  1021. }
  1022.  
  1023. /* Draw three borders of the given cell. */
  1024.  
  1025. /* (do we need another routine to repair all six borders?) */
  1026.  
  1027. static void
  1028. draw_borders(side, map, x, y, b)
  1029. Side *side;
  1030. Map *map;
  1031. int x, y, b;
  1032. {
  1033.     int dir, bitmask = 0, sx, sy;
  1034.     
  1035.     if (!terrain_visible(side, wrapx(x), y)
  1036.     || !any_borders_at(x, y, b))
  1037.       return;
  1038.     for_all_directions(dir) {
  1039.     if (border_at(x, y, dir, b) && seen_border(side, x, y, dir)) {
  1040.         bitmask |= 1 << dir;
  1041.     }
  1042.     }
  1043.     if (bitmask != 0) {
  1044.     xform(side, map, x, y, &sx, &sy);
  1045.     draw_border_line_multiple(side, map, map->viewwin, sx, sy,
  1046.                   bitmask, map->vp->power, b);
  1047.     }
  1048. }
  1049.  
  1050. /* Draw three connections of the given cell. */
  1051.  
  1052. /* Actually this draws all six half-connections.  It also only draws the
  1053.    connection if the underlying terrain is different. */
  1054.  
  1055. static void
  1056. draw_connections(side, map, x, y, t)
  1057. Side *side;
  1058. Map *map;
  1059. int x, y, t;
  1060. {
  1061.     int dir, bitmask = 0, sx, sy;
  1062.  
  1063.     if (bwid[map->vp->power] == 0)
  1064.       return;
  1065.     if (!terrain_visible(side, x, y))
  1066.       return;
  1067.     xform(side, map, x, y, &sx, &sy);
  1068.     for_all_directions(dir) {
  1069.     if (connection_at(x, y, dir, t)) {
  1070.         bitmask |= 1 << dir;
  1071.     }
  1072.     }
  1073.     if (bitmask != 0) {
  1074.     draw_connection_line_multiple(side, map->viewwin,
  1075.                       sx, sy, bitmask,
  1076.                       map->vp->power, t);
  1077.     }
  1078. }
  1079.  
  1080. /* Draw any text that should be associated with this cell. */
  1081.  
  1082. /* (could precompute what the string will lap over and move or truncate str),
  1083.    should be deterministic for each mag, so redraw doesn't scramble */
  1084.  
  1085. /* do features, label at a cell with nothing else, and declared as the
  1086.    feature's "center" */
  1087.  
  1088. /* Black/white text should be consistent for each period, use mask only
  1089.    to fix difficulties. */
  1090.  
  1091. static void
  1092. draw_legend(side, map, x, y)
  1093. Side *side;
  1094. Map *map;
  1095. int x, y;
  1096. {
  1097.     int sx, sy, pixlen;
  1098.     char *featname;
  1099.     Feature *feature;
  1100.  
  1101.     if (!inside_area(x, y))
  1102.       return;
  1103.     if (!terrain_visible(side, x, y))
  1104.       return;
  1105.     /* feature object should specify legend's position */
  1106.     feature = feature_at(x, y);
  1107.     if (feature != NULL) {
  1108.     if (feature->size == 1) {
  1109.         featname = feature_name_at(x, y);
  1110.         if (featname != NULL) {
  1111.         pixlen = strlen(featname) * 8;
  1112.         xform(side, map, x, y, &sx, &sy);
  1113.         draw_legend_text(side, map->viewwin,
  1114.                  sx + 1 + (pixlen > map->vp->hw ? 2 :
  1115.                        (map->vp->hw/2 - pixlen / 2)),
  1116.                  sy - 6 + map->vp->hh / 2,
  1117.                  map->vp->power, featname,
  1118.                  side->ui->fgcolor, TRUE);
  1119.         }
  1120.     }
  1121.     }
  1122. }
  1123.  
  1124. /* Cursor drawing also draws the unit in some other color if it's not the
  1125.    "top-level" unit in a cell. */
  1126.  
  1127. /* "color unders" here are not correct */
  1128.  
  1129. void
  1130. draw_current(side, map)
  1131. Side *side;
  1132. Map *map;
  1133. {
  1134.     int sx, sy, sw, sh;
  1135.     int uw = map->vp->uw, uh = map->vp->uh;
  1136.     Unit *unit;
  1137.  
  1138.     if (in_play(map->curunit)) {
  1139.     unit = map->curunit;
  1140.     /* Compute the bounding box we're going to hilite. */
  1141.     if (map->vp->power >= 5) { /* not ideal test */
  1142.         x_xform_unit_self(side, map, unit, &sx, &sy, &sw, &sh);
  1143.     } else {
  1144.         xform(side, map, unit->x, unit->y, &sx, &sy);
  1145.         /* Adjust to unit part of cell. */
  1146.         sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
  1147.         sw = uw;  sh = uh;
  1148.     }
  1149.     /* Maybe redraw the unit that the cursor is showing. */
  1150.     if (unit->transport != NULL) {
  1151.         if (side->ui->monochrome) {
  1152.         draw_unit_image(side, map->viewwin, sx, sy, sw, sh,
  1153.                 unit->type, -1, -1, -1);
  1154.         } else {
  1155.         /* Leave any underlying image alone, but draw over it
  1156.            in a different color. */
  1157.         draw_unit_image(side, map->viewwin, sx, sy, sw, sh,
  1158.                 unit->type, -1, side->ui->diffcolor, -1);
  1159.         }
  1160.     }
  1161.     /* Draw the cursor icon proper. */
  1162.     draw_cursor_icon(side, map->viewwin, sx, sy, sw, sh);
  1163.     } else if (inside_area(map->curx, map->cury)) {
  1164.     xform(side, map, map->curx, map->cury, &sx, &sy);
  1165.     /* Adjust to unit part of cell. */
  1166.     sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
  1167.     draw_cursor_icon(side, map->viewwin, sx, sy, uw, uh);
  1168.     }
  1169. }
  1170.  
  1171. /* Get rid of curunit indicator by redrawing the cell. */
  1172.  
  1173. void
  1174. erase_current(side, map, x, y, unit)
  1175. Side *side;
  1176. Map *map;
  1177. int x, y;
  1178. Unit *unit;
  1179. {
  1180.     /* (should use unit to decide whether to redraw only its part of
  1181.        the cell, instead of doing whole cell) */
  1182.     if (in_area(x, y)) {
  1183.     draw_row(side, map, x, y, 1, -1);
  1184.     } else if (unit != NULL && in_area(unit->x, unit->y)) {
  1185. #if 0 /* this would be a good optimization if it were fixed... */
  1186.     draw_one_unit(side, map, unit);
  1187. #else
  1188.     draw_row(side, map, unit->x, unit->y, 1, -1);
  1189. #endif
  1190.     }
  1191. }
  1192.  
  1193. #if 0
  1194. /* Draw a splat visible to both sides at a given location.  Several splats
  1195.    available, depending on the seriousness of the hit.  Make an extra-flashy
  1196.    display when The Bomb goes off.  Because of the time delays involved, we
  1197.    have to update both sides' displays more or less simultaneously.  */
  1198.  
  1199. void
  1200. draw_blast(unit, es, hit)
  1201. Unit *unit;
  1202. Side *es;
  1203. int hit;
  1204. {
  1205. }
  1206. #endif
  1207.  
  1208. #if 0
  1209. /* Flash the player's screen in an unmistakable way. */
  1210.  
  1211. invert_whole_map(side, map)
  1212. Side *side;
  1213. Map *map;
  1214. {
  1215.     int sw = map->vw * map->vp->hw, sh = map->vh * map->vp->hh;
  1216.  
  1217.     XFillRectangle(side->ui->dpy, map->viewwin, side->ui->gc,
  1218.            0, 0, sw, sh);
  1219.     flush_output(side);
  1220. }
  1221. #endif
  1222.  
  1223. #if 0
  1224. /* Draw just one of the mushroom cloud shapes. */
  1225.  
  1226. draw_mushroom(side, map, x, y, i)
  1227. Side *side;
  1228. Map *map;
  1229. int x, y, i;
  1230. {
  1231. }
  1232. #endif
  1233.  
  1234. static void
  1235. draw_feature_boundary(side, map, x, y, fid)
  1236. Side *side;
  1237. Map *map;
  1238. int x, y, fid;
  1239. {
  1240.     int wid, p, wid2, d, color, fid0, x1, y1, sx, sy;
  1241.     Display *dpy = side->ui->dpy;
  1242.     GC gc = side->ui->terrgc;
  1243.     Pixmap graylev;
  1244.  
  1245.     p = map->vp->power;
  1246.     wid = bwid[p];
  1247.     fid0 = raw_feature_at(x, y);
  1248.     if (fid0 == 0)
  1249.       return;
  1250.     xform(side, map, x, y, &sx, &sy);
  1251.     if (wid == 0)
  1252.       return;
  1253.     wid2 = wid / 2;
  1254.  
  1255.     /* for now: */
  1256.     if (fid0 == fid) {
  1257.     color = side->ui->enemycolor;
  1258.     } else {
  1259.     color = side->ui->neutcolor;
  1260.     }
  1261.     XSetForeground(dpy, gc, color);
  1262.     if (side->ui->monochrome) {
  1263.     if (fid0 == fid) {
  1264.         graylev = side->ui->grays[darkgray];
  1265.     } else {
  1266.         graylev = side->ui->grays[gray];
  1267.     }
  1268.     XSetFillStyle(dpy, gc, FillStippled);
  1269.     XSetStipple(dpy, gc, graylev);
  1270.     }
  1271.     XSetClipMask(dpy, gc, None);
  1272.     XSetLineAttributes(dpy, gc, bwid[p], LineSolid, CapButt, JoinMiter); 
  1273.     for_all_directions(d) {
  1274.     if (point_in_dir(x, y, d, &x1, &y1)) {
  1275.         if (raw_feature_at(x1,y1) != fid0) {
  1276.         XDrawLine(dpy, map->viewwin, gc,
  1277.               sx + qx[p][d], sy + qy[p][d],
  1278.               sx + qx[p][d+1], sy + qy[p][d+1]);
  1279.         }
  1280.     }
  1281.     }
  1282. }
  1283.  
  1284. /* Do the grody work of drawing very large polygons accurately. */
  1285.  
  1286. static void
  1287. draw_hex_polygon(side, map, win, gc, sx, sy, power, dogrid)
  1288. Side *side;
  1289. Map *map;
  1290. Window win;
  1291. GC gc;
  1292. int sx, sy, power, dogrid;
  1293. {
  1294.     XPoint points[6];
  1295.     int hw = hws[power], hh = hhs[power], delt = (hhs[power] - hcs[power]);
  1296.     int ew = (dogrid ? 1 : 0);
  1297.         
  1298.     points[0].x = sx + hw / 2;        points[0].y = sy;
  1299.     points[1].x = hw / 2 - ew;        points[1].y = delt - ew;
  1300.     points[2].x = 0;                  points[2].y = hh - 2 * delt - ew;
  1301.     points[3].x = 0 - (hw / 2 - ew);  points[3].y = delt - ew;
  1302.     points[4].x = 0 - (hw / 2 - ew);  points[4].y = 0 - (delt - ew);
  1303.     points[5].x = 0;                  points[5].y = 0 - (hh - 2 * delt - ew);
  1304.     XFillPolygon(side->ui->dpy, win, gc, points, 6, Convex, CoordModePrevious);
  1305.     XFlush(side->ui->dpy);
  1306. }
  1307.  
  1308. /* Draw a mask of borders for the given location. */
  1309.  
  1310. static void
  1311. draw_border_line_multiple(side, map, win, sx, sy, bitmask, power, t)
  1312. Side *side;
  1313. Map *map;
  1314. Window win;
  1315. int sx, sy, bitmask, power, t;
  1316. {
  1317.     int wid = bwid[power], wid2, dir, color, sx1, sy1, sx2, sy2;
  1318.     Image *timg;
  1319.     Display *dpy = side->ui->dpy;
  1320.     GC gc = side->ui->terrgc;
  1321.  
  1322.     if (wid == 0)
  1323.       return;
  1324.     wid2 = wid / 2;
  1325.     color = side->ui->cellcolor[t];
  1326.     XSetForeground(dpy, gc, color);
  1327.     XSetBackground(dpy, gc, side->ui->whitecolor);
  1328.     timg = best_image(side->ui->timages[t], wid, wid);
  1329.     set_terrain_gc_for_image(side, timg);
  1330.     XSetClipMask(dpy, gc, None);
  1331.     XSetLineAttributes(dpy, gc, bwid[power], LineSolid, CapButt, JoinMiter); 
  1332.  
  1333.     for_all_directions(dir) {
  1334.     if (bitmask & (1 << dir)) {
  1335.         sx1 = bsx[power][dir];  sy1 = bsy[power][dir];
  1336.         sx2 = bsx[power][dir+1];  sy2 = bsy[power][dir+1];
  1337.         XDrawLine(dpy, win, gc,
  1338.               sx + sx1 - wid2, sy + sy1 - wid2,
  1339.               sx + sx2 - wid2, sy + sy2 - wid2);
  1340.     }
  1341.     }
  1342. }
  1343.  
  1344. /* Draw a mask of connection terrain at the given location. */
  1345.  
  1346. static void
  1347. draw_connection_line_multiple(side, win, sx, sy, bitmask, power, t)
  1348. Side *side;
  1349. Window win;
  1350. int sx, sy, bitmask, power, t;
  1351. {
  1352.     int wid = cwid[power], wid2, cx = hws[power] / 2, cy = hhs[power] / 2;
  1353.     int color, dir;
  1354.     Image *timg;
  1355.     Display *dpy = side->ui->dpy;
  1356.     GC gc = side->ui->terrgc;
  1357.  
  1358.     if (wid == 0 || lsx[power][0] == 0)
  1359.       return;
  1360.     wid2 = wid / 2;
  1361.     color = side->ui->cellcolor[t];
  1362.     XSetForeground(dpy, gc, color);
  1363.     XSetBackground(dpy, gc, side->ui->whitecolor);
  1364.     timg = best_image(side->ui->timages[t], wid, wid);
  1365.     set_terrain_gc_for_image(side, timg);
  1366.     XSetClipMask(dpy, gc, None);
  1367.     XSetLineAttributes(dpy, gc, wid, LineSolid, CapButt, JoinMiter); 
  1368.  
  1369.     for_all_directions(dir) {
  1370.     if (bitmask & (1 << dir)) {
  1371.         XDrawLine(dpy, win, gc,
  1372.               sx + cx - wid2, sy + cy - wid2,
  1373.               sx + cx + lsx[power][dir] - wid2,
  1374.               sy + cy + lsy[power][dir] - wid2);
  1375.     }
  1376.     }
  1377. }
  1378.  
  1379. /* Map legends are stencils usually. */
  1380.  
  1381. static void
  1382. draw_legend_text(side, win, sx, sy, power, str, color, maskit)
  1383. Side *side;
  1384. Window win;
  1385. int sx, sy, power, color, maskit;
  1386. char *str;
  1387. {
  1388.     sy += (side->ui->ulegendfonts[power][0])->max_bounds.ascent;
  1389.     XSetFont(side->ui->dpy, side->ui->ltextgc,
  1390.          (side->ui->ulegendfonts[power][0])->fid);
  1391.     XSetForeground(side->ui->dpy, side->ui->ltextgc, color);
  1392.     if (maskit) {
  1393.     XSetBackground(side->ui->dpy, side->ui->ltextgc, 
  1394.                (color == side->ui->bgcolor ? side->ui->fgcolor :
  1395.             side->ui->bgcolor));
  1396.     XDrawImageString(side->ui->dpy, win, side->ui->ltextgc,
  1397.              sx, sy, str, strlen(str));
  1398.     } else {
  1399.     XDrawString(side->ui->dpy, win, side->ui->ltextgc,
  1400.             sx, sy, str, strlen(str));
  1401.     }
  1402. }
  1403.  
  1404. /* Splash a unit image (either bitmap or font char) onto some window. */
  1405.  
  1406. void
  1407. draw_unit_image(side, win, sx, sy, sw, sh, u, s2, fg, bg)
  1408. Side *side;
  1409. Window win;
  1410. int sx, sy, sw, sh, u, s2, fg, bg;
  1411. {
  1412.     char buf[1];
  1413.     int sx2, sy2, sw2, sh2, ex, ey, ew, eh, desperate = FALSE;
  1414.     long imagecolor, maskcolor;
  1415.     Image *uimg;
  1416.     X11Image *ximg;
  1417.     Display *dpy = side->ui->dpy;
  1418.     GC gc = side->ui->unitgc;
  1419.  
  1420.     uimg = best_image(side->ui->uimages[u], sw, sh);
  1421.     if (uimg) {
  1422.     /* Offset the image to draw in the middle of its area,
  1423.        whether larger or smaller than the given area. */
  1424.     sx2 = sx + (sw - uimg->w) / 2;  sy2 = sy + (sh - uimg->h) / 2;
  1425.     /* Only change the size of the rectangle being drawn if it's
  1426.        smaller than what was passed in. */
  1427.     if (uimg->w < sw) {
  1428.         sx = sx2;
  1429.         sw = uimg->w;
  1430.     }
  1431.     if (uimg->h < sh) {
  1432.         sy = sy2;
  1433.         sh = uimg->h;
  1434.     }
  1435.     /* Figure out what colors to use. */
  1436.     imagecolor = ((fg != -1) ? fg : side->ui->blackcolor);
  1437.     if (side->ui->unitcolors) {
  1438. #if 0
  1439.       (side->ui->numcolors[s2] > 2) ? side->ui->colors[s2][2] :
  1440.         (side->ui->numcolors[s2] > 0) ? side->ui->colors[s2][0] :
  1441. #endif
  1442.     }
  1443.     maskcolor = ((bg != -1) ? bg : side->ui->whitecolor);
  1444.     if (side->ui->unitcolors) {
  1445. #if 0
  1446.       (side->ui->numcolors[s2] > 1) ? side->ui->colors[s2][1] :
  1447.         side->ui->whitecolor;
  1448. #endif
  1449.     }
  1450.     ximg = (X11Image *) uimg->hook;
  1451.     if (ximg != NULL) {
  1452.         if (!side->ui->monochrome
  1453.         && side->ui->dflt_color_unit_images
  1454.         && ximg->colr != None) {
  1455.         if (ximg->mask != None) {
  1456.             /* set the clip mask */
  1457.             XSetClipOrigin(dpy, gc, sx2, sy2);
  1458.             XSetClipMask(dpy, gc, ximg->mask);
  1459.         }
  1460.         /* Draw the color image. */
  1461.         XCopyArea(dpy, ximg->colr, win, gc, 0, 0, sw, sh, sx, sy);
  1462.         } else if (ximg->mono != None || ximg->mask != None) {
  1463.         /* Set the origin for any subsequent clipping. */
  1464.         XSetClipOrigin(dpy, gc, sx2, sy2);
  1465.         /* Set the color we're going to use for the mask; use
  1466.            the imagecolor if we'll be using the mask as the
  1467.            only image. */
  1468.         XSetForeground(dpy, gc,
  1469.                    (ximg->mono == None ? imagecolor : maskcolor));
  1470.         /* Set the clip mask to be explicit mask or unit's image. */
  1471.         if (ximg->mask)
  1472.           XSetClipMask(dpy, gc, ximg->mask);
  1473.         else
  1474.           XSetClipMask(dpy, gc, ximg->mono);
  1475.         /* Draw the mask. */
  1476.         XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
  1477.         /* Draw the image proper. */
  1478.         if (ximg->mono != None) {
  1479.             XSetForeground(dpy, gc, imagecolor);
  1480.             XSetClipMask(dpy, gc, ximg->mono);
  1481.             XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
  1482.         }
  1483.         } else if (ximg->monochar) {
  1484.         XSetForeground(dpy, gc, imagecolor);
  1485.         buf[0] = ximg->monochar;
  1486.         /*        sy += uimg->font->max_bounds.ascent;  */
  1487.         /* need to set font as the one in use here? */
  1488.         XDrawString(dpy, win, gc, sx, sy, buf, 1);
  1489.         }
  1490.     } else
  1491.       desperate = TRUE;
  1492.     } else
  1493.       desperate = TRUE;
  1494.     if (desperate) {
  1495.     /* a bad error? */
  1496.     XSetClipOrigin(dpy, gc, sx, sy);
  1497.     /* Pretty desperate, or else we're at a scale that doesn't matter. */
  1498.     /* should actually clip to cell boundaries, maybe even inside
  1499.        border terrain if necessary? */
  1500.     XSetClipMask(dpy, gc, None);
  1501.     /* Draw an edge, at least for larger mags. */
  1502.     if (sw >= 4) {
  1503.         XSetForeground(dpy, gc, maskcolor);
  1504.         XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
  1505.     }
  1506.     /* Draw a filled box inside the edge. */
  1507.     XSetForeground(dpy, gc, imagecolor);
  1508.     XFillRectangle(dpy, win, gc, sx + 1, sy + 1, sw - 2, sh - 2);
  1509.     /* Might be cool to draw unit type name or char */
  1510.     }
  1511.     if (between(0, s2, numsides)) {
  1512.     if (1 /* emblem is not already part of image */) {
  1513.         if (0 /* size is specified with image */) {
  1514.         } else {
  1515.         ew = min(sw, max(8, sw / 4));  eh = min(sh, max(8, sh / 4));
  1516.         }
  1517.         if (0 /* pos is specified with image */) {
  1518.         } else {
  1519.         ex = sw - ew;  ey = 0;
  1520.         }
  1521.         draw_side_emblem(side, win, sx + ex, sy + ey, ew, eh, s2, 0);
  1522.     }
  1523.     }
  1524. }
  1525.  
  1526. /* Draw an emblem identifying the given side.  If a side does not have a
  1527.    distinguishing emblem, fall back on some defaults. */
  1528.  
  1529. void
  1530. draw_side_emblem(side, win, ex, ey, ew, eh, s2, style)
  1531. Side *side;
  1532. Window win;
  1533. int ex, ey, ew, eh, s2, style;
  1534. {
  1535.     long imagecolor, maskcolor;
  1536.     Image *img;
  1537.     X11Image *ximg;
  1538.     Display *dpy = side->ui->dpy;
  1539.     GC gc = side->ui->emblgc;
  1540.  
  1541.     /* Draw the emblem's mask, or else an enclosing box. */
  1542.     img = best_image(side->ui->eimages[s2], ew, eh);
  1543.     if (style == 1) {
  1544.     XSetForeground(dpy, gc, side->ui->fgcolor);
  1545.     XDrawRectangle(dpy, win, gc, ex - 1, ey - 1, ew + 2, eh + 2);
  1546.     }
  1547.     /* If an image is available, display it, otherwise do nothing. */
  1548.     if (img != NULL) {
  1549.     ximg = (X11Image *) img->hook;
  1550.     /* Decide on the colors to use with the emblem. */
  1551.     if (side->ui->numcolors[s2] > 0) {
  1552.         imagecolor = side->ui->colors[s2][0];
  1553.     } else {
  1554.         imagecolor = side->ui->blackcolor;
  1555.     }
  1556.     if (side->ui->numcolors[s2] > 1) {
  1557.         maskcolor = side->ui->colors[s2][1];
  1558.     } else {
  1559.         maskcolor = side->ui->whitecolor;
  1560.     }
  1561.     /* Draw the mask. */
  1562.     XSetForeground(dpy, gc, maskcolor);
  1563.     XSetClipOrigin(dpy, gc, ex, ey);
  1564.     XSetClipMask(dpy, gc, (ximg != NULL ? ximg->mask : None));
  1565.     XFillRectangle(dpy, win, gc, ex, ey, ew, eh);
  1566.     /* Now draw the emblem proper. */
  1567.     /* (should fix so some color emblems can be used with mono displays) */
  1568.     if (!side->ui->monochrome
  1569.         && side->ui->dflt_color_embl_images
  1570.         && ximg != NULL
  1571.         && ximg->colr != None) {
  1572.         /* Draw the color image. */
  1573.         XCopyArea(dpy, ximg->colr, win, gc, 0, 0, ew, eh, ex, ey);
  1574.     } else {
  1575.         XSetForeground(dpy, gc, imagecolor);
  1576.         XSetClipMask(dpy, gc, (ximg != NULL ? ximg->mono : None));
  1577.         XFillRectangle(dpy, win, gc, ex, ey, ew, eh);
  1578.     }
  1579.     }
  1580. }
  1581.  
  1582. static void
  1583. draw_country_border_line(side, win, sx, sy, dir, power)
  1584. Side *side;
  1585. Window win;
  1586. int sx, sy, dir, power;
  1587. {
  1588.     int wid = bwid[power];
  1589.  
  1590.     if (wid == 0)
  1591.       return;
  1592.     wid = max(1, wid / 2);
  1593.     XSetForeground(side->ui->dpy, side->ui->terrgc, side->ui->whitecolor);
  1594.     XSetClipMask(side->ui->dpy, side->ui->terrgc, None);
  1595.     XSetLineAttributes(side->ui->dpy, side->ui->terrgc,
  1596.                wid, LineSolid, CapButt, JoinMiter); 
  1597.     XDrawLine(side->ui->dpy, win, side->ui->terrgc,
  1598.           sx + bsx[power][dir], sy + bsy[power][dir],
  1599.           sx + bsx[power][dir+1], sy + bsy[power][dir+1]);
  1600. }
  1601.  
  1602. /* The cursor icon highlights a unit of interest - should be called a
  1603.    "selection highlight" or something similar. */
  1604.  
  1605. static void
  1606. draw_cursor_icon(side, win, sx, sy, sw, sh)
  1607. Side *side;
  1608. Window win;
  1609. int sx, sy, sw, sh;
  1610. {
  1611.     Display *dpy = side->ui->dpy;
  1612.     GC gc = side->ui->unitgc;
  1613.  
  1614.     if (sw == 16 && sh == 16) {
  1615.     /* At 16x16, use a special pair of bitmaps. */
  1616.     XSetClipOrigin(dpy, gc, sx, sy);
  1617.     XSetForeground(dpy, gc, side->ui->whitecolor);
  1618.     XSetClipMask(dpy, gc, side->ui->boxmask);
  1619.     XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
  1620.     XSetForeground(dpy, gc, side->ui->blackcolor);
  1621.     XSetClipMask(dpy, gc, side->ui->boxcurs);
  1622.     XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
  1623.     } else {
  1624.     /* Box at larger sizes need to come in slightly, otherwise we
  1625.        get "dirt" pixels in the hex grid. */
  1626.     if (sw > 16 && sh > 16)
  1627.       sh -= 1;
  1628.     XSetClipMask(dpy, gc, None);
  1629.     XSetForeground(dpy, gc, side->ui->whitecolor);
  1630.     XDrawRectangle(dpy, win, gc, sx, sy, sw - 1, sh - 1);
  1631.     XSetForeground(dpy, gc, side->ui->blackcolor);
  1632.     XDrawRectangle(dpy, win, gc, sx + 1, sy + 1, sw - 3, sh - 3);
  1633.     /* If the box is not too small, draw an inner white rectangle. */
  1634.     if (sw > 4 && sh > 4) {
  1635.         XSetForeground(dpy, gc, side->ui->whitecolor);
  1636.         XDrawRectangle(dpy, win, gc, sx + 2, sy + 2, sw - 5, sh - 5);
  1637.     }
  1638.     }
  1639. }
  1640.  
  1641. /* Describe the state of the given unit, in maximal detail. */
  1642.  
  1643. void
  1644. draw_map_info(side, map)
  1645. Side *side;
  1646. Map *map;
  1647. {
  1648.     char infobuf[BUFSIZE], *featurename;
  1649.     int i, u, s, m, t, mrow, nums[MAXUTYPES];
  1650.     int x = map->curx, y = map->cury, uview;
  1651.     Unit *unit, *occ, *mainunit;
  1652.     Side *side2;
  1653.  
  1654.     XClearWindow(side->ui->dpy, map->infowin);
  1655.     unit = map->curunit;
  1656.     if (!in_play(unit)) {
  1657.     if (inside_area(x, y)) {
  1658.         t = terrain_at(x, y);
  1659.         u = NONUTYPE;
  1660.         if (map->seeall) {
  1661.         unit = unit_at(x, y);
  1662.         if (unit != NULL) {
  1663.             u = unit->type;
  1664.             side2 = unit->side;
  1665.         }
  1666.         } else {
  1667.         uview = unit_view(side, x, y);
  1668.         if (uview != EMPTY) {
  1669.             u = vtype(uview);  s = vside(uview);
  1670.             side2 = side_n(s);
  1671.         }
  1672.         }
  1673.         if (u != NONUTYPE) {
  1674.         sprintf(infobuf, "%s %s",
  1675.                    side_adjective(side2), u_type_name(u));
  1676.         draw_info_text(side, map, 0, 0, 40, infobuf);
  1677.         sprintf(infobuf, "In %s", t_type_name(t));
  1678.         } else if (terrain_visible(side, x, y)) {
  1679.         sprintf(infobuf, "Empty %s", t_type_name(t));
  1680.         } else {
  1681.         sprintf(infobuf, "Unknown");
  1682.         }
  1683.         if (terrain_visible(side, x, y)) {
  1684.         linear_desc(infobuf, x, y);
  1685.         featurename = feature_name_at(x, y);
  1686.         if (!empty_string(featurename))
  1687.           tprintf(infobuf, " (%s)", featurename);
  1688.         if (temperatures_defined())
  1689.           tprintf(infobuf, " (T %d)", temperature_at(x, y));
  1690.         if (elevations_defined())
  1691.           tprintf(infobuf, " (El %d)", elev_at(x, y));
  1692.         /* (should list local weather also) */
  1693.         }
  1694.         tprintf(infobuf, " at %d,%d", x, y);
  1695.         draw_info_text(side, map, 0, 1, 40, infobuf);
  1696.     } else {
  1697.         /* (should never happen?) */
  1698.     }
  1699.     return;
  1700.     }
  1701.     u = unit->type;
  1702.     /* Say which unit this is. */
  1703.     sprintf(infobuf, "%s", unit_handle(side, unit));
  1704.     draw_info_text(side, map, 0, 0, 40, infobuf);
  1705.     /* Mention its transport (or underlying terrain) if there is one. */
  1706.     if (unit->transport != NULL) {
  1707.     sprintf(infobuf, "In %s", short_unit_handle(unit->transport));
  1708.     } else {
  1709.     sprintf(infobuf, "In %s", t_type_name(terrain_at(x, y)));
  1710.     linear_desc(infobuf, x, y);
  1711.     tmpbuf[0] = '\0';
  1712.     featurename = feature_name_at(x, y);
  1713.     if (!empty_string(featurename))
  1714.       sprintf(tmpbuf, " (%s)", featurename);
  1715.     if (temperatures_defined())
  1716.       tprintf(tmpbuf, " (T %d)", temperature_at(x, y));
  1717.     if (elevations_defined())
  1718.       tprintf(tmpbuf, " (El %d)", elev_at(x, y));
  1719.     }
  1720.     tprintf(infobuf, " at %d,%d", x, y);
  1721.     draw_info_text(side, map, 0, 1, 40, infobuf);
  1722.     /* Very briefly list the numbers and types of the occupants. */
  1723.     infobuf[0] = '\0';
  1724.     if (unit->occupant != NULL) {
  1725.     strcpy(infobuf, "Occ ");
  1726.     for_all_unit_types(i) nums[i] = 0;
  1727.     for_all_occupants(unit, occ) ++nums[occ->type];
  1728.     for_all_unit_types(i) {
  1729.         if (nums[i] > 0) {
  1730.         tprintf(infobuf, "%d %1s  ", nums[i], utype_name_n(i, 1));
  1731.         }
  1732.     }
  1733.     }
  1734.     draw_info_text(side, map, 0, 2, 40, infobuf);
  1735.     /* Display the "important" parameters. */
  1736.     /* (should say something about parts?) */
  1737.     hp_desc(infobuf, unit, TRUE);
  1738.     strcat(infobuf, "   ");
  1739.     acp_desc(tmpbuf, unit, TRUE);
  1740.     strcat(infobuf, tmpbuf);
  1741.     draw_info_text(side, map, 50, 0, -1, infobuf);
  1742.     /* (should display cxp and morale here also, if defined) */
  1743.     infobuf[0] = '\0';
  1744.     /* make this a "stacked_at" macro? */
  1745.     mainunit = unit_at(x, y);
  1746.     if (mainunit != NULL && mainunit->nexthere != NULL) {
  1747.     strcpy(infobuf, "Others here  ");
  1748.     for_all_unit_types(i)
  1749.       nums[i] = 0;
  1750.     for_all_stack(x, y, occ)
  1751.       ++nums[occ->type];
  1752.     for_all_unit_types(i) {
  1753.         if (nums[i] > 0) {
  1754.         tprintf(infobuf, "%d %1s  ", nums[i], utype_name_n(i, 1));
  1755.         }
  1756.     }
  1757.     }
  1758.     if (strlen(infobuf) > 0)
  1759.       draw_info_text(side, map, 50, 1, -1, infobuf);
  1760.     /* Describe the state of all the supplies. */
  1761.     /* Start on the third line (leaving the second line blank, for
  1762.        readability), unless there are lots of supply types that we'll
  1763.        need to display. */
  1764.     mrow = 2; /* (nummtypes > 4 ? 1 : 2)  but need to account for also heres */
  1765.     infobuf[0] = '\0';
  1766.     for_all_material_types(m) {
  1767.     if (um_storage_x(u, m) > 0) {
  1768.         tprintf(infobuf, "%s %d/%d  ",
  1769.             m_type_name(m), unit->supply[m], um_storage_x(u, m));
  1770.         if (m > 0 && m % 2 == 0) {
  1771.         draw_info_text(side, map, 50, mrow++, -1, infobuf);
  1772.         infobuf[0] = '\0';
  1773.         }
  1774.     }
  1775.     }
  1776.     /* Do the last row of supply info, if any left to put out. */
  1777.     if (strlen(infobuf) > 0)
  1778.       draw_info_text(side, map, 50, mrow, -1, infobuf);
  1779.     /* Describe the current plans, tasks, etc. */
  1780.     if (unit->plan) {
  1781.     plan_desc(infobuf, unit);
  1782.     /* (should be on two lines) */
  1783.     draw_info_text(side, map, 0, 3, 40, infobuf);
  1784.     }
  1785. }
  1786.  
  1787. /* Display improvement can be achieved by padding out lines with blanks,
  1788.    then the lines need not be cleared before redrawing. */
  1789.  
  1790. static void
  1791. draw_info_text(side, map, x, y, len, buf)
  1792. Side *side;
  1793. Map *map;
  1794. int x, y, len;
  1795. char *buf;
  1796. {
  1797.     int sx, sy;
  1798.  
  1799.     /* Translate a 0-100 value for x to pixels. */
  1800.     if (x == 50)
  1801.       sx = map->pxw / 2;
  1802.     else
  1803.       sx = 2;
  1804.     sy = y * side->ui->fh;
  1805.     if (len > 0 && strlen(buf) > len)
  1806.       buf[len-1] = '\0';
  1807.     draw_text(side, map->infowin, sx, sy, buf, side->ui->fgcolor);
  1808. }
  1809.  
  1810. /* Write onto the list of sides. */
  1811.  
  1812. void
  1813. draw_map_sides(side, map)
  1814. Side *side;
  1815. Map *map;
  1816. {
  1817.     Side *side2;
  1818.  
  1819.     if (map == NULL)
  1820.       return;
  1821.     for_all_sides(side2) {
  1822.     draw_side_info(side, map, side2);
  1823.     }
  1824. }
  1825.  
  1826. /* Show info about a single side to some other side. */
  1827.  
  1828. void
  1829. draw_side_info(side, map, side2)
  1830. Side *side, *side2;
  1831. Map *map;
  1832. {
  1833.     char tmpbuf[BUFSIZE];
  1834.     int sx, sy, fh = side->ui->fh;
  1835.     Display *dpy = side->ui->dpy;
  1836.     GC gc = side->ui->gc;
  1837.  
  1838.     if (map == NULL || side2 == NULL)
  1839.       return;
  1840.     sx = 2 + 16 + 2;
  1841.     sy = (side_number(side2) - 1) * map->sidespacing;
  1842.     draw_side_emblem(side, map->sideswin, 2 + 2, sy + 4, 8, 8,
  1843.              side_number(side2), 1);
  1844.     /* Build up and write the textual description of the side. */
  1845.     tmpbuf[0] = '\0';
  1846. #ifdef DESIGNERS
  1847.     if (side2->designer)
  1848.       strcat(tmpbuf, "(designer)");
  1849. #endif /* DESIGNERS */
  1850.     strcat(tmpbuf, short_side_title(side2));
  1851.     if (side2->player) {
  1852.     strcat(tmpbuf, "(");
  1853.     strcat(tmpbuf, player_desig(side2->player));
  1854.     strcat(tmpbuf, ")");
  1855.     }
  1856.     draw_text(side, map->sideswin, sx, sy, tmpbuf,
  1857.               (side2 == side ? side->ui->bgcolor : side->ui->fgcolor));
  1858.     if (side_won(side2)) {
  1859.     /* what to do here? */
  1860.     } else if (side_lost(side2)) {
  1861.     /* Draw the line of ignominy through sides that have lost. */
  1862.     XSetForeground(dpy, gc, side->ui->fgcolor);
  1863.     XDrawLine(dpy, map->sideswin, gc,
  1864.           0, sy + fh / 2, 500, sy + fh / 2);
  1865.     } else if (side2->ingame) {
  1866.     draw_side_progress(side, map, side2);
  1867.     } else {
  1868.     /* what to do here? */
  1869.     }
  1870. }
  1871.  
  1872. /* Display how far along the side is with moving its units. */
  1873.  
  1874. void
  1875. draw_side_progress(side, map, side2)
  1876. Side *side, *side2;
  1877. Map *map;
  1878. {
  1879.     int sx, sy, totacp, left = 0;
  1880.     Display *dpy = side->ui->dpy;
  1881.     GC gc = side->ui->gc;
  1882.  
  1883.     if (map == NULL || side2 == NULL)
  1884.       return;
  1885.     sx = 2 + 16 + 2;
  1886.     sy = (side_number(side2) - 1) * map->sidespacing;
  1887.     XSetClipMask(dpy, gc, None);
  1888.     totacp = side_initacp(side2);
  1889.     if (totacp > 0) {
  1890.     left = (100 * side_acp(side2)) / totacp;
  1891.     left = max(0, min(100, left));
  1892.     }
  1893.     /* Clear the bar. */
  1894.     XSetForeground(dpy, gc, side->ui->bgcolor);
  1895.     XFillRectangle(dpy, map->sideswin, gc,
  1896.            sx + 1 + 0, sy + side->ui->fh + 2, 100, 10);
  1897.  
  1898.     /* And show what we haven't used yet... */
  1899.     if (left > 0) {
  1900.     XSetForeground(dpy, gc, side->ui->fgcolor);
  1901.     if (side2->finishedturn
  1902.         || !(side_has_ai(side2) || side_has_display(side2))) {
  1903.         XSetFillStyle(dpy, gc, FillOpaqueStippled);
  1904.         XSetStipple(dpy, gc, side->ui->grays[gray]);
  1905.     }
  1906.     XFillRectangle(dpy, map->sideswin, gc,
  1907.                sx + 1 + 0, sy + side->ui->fh + 2, left, 10);
  1908.     if (side2->finishedturn
  1909.         || !(side_has_ai(side2) || side_has_display(side2))) {
  1910.         XSetFillStyle(dpy, gc, FillSolid);
  1911.     }
  1912.     }
  1913.  
  1914.     /* Always frame the progress bar. */
  1915.     XSetForeground(dpy, gc, side->ui->fgcolor);
  1916.     XDrawRectangle(dpy, map->sideswin, gc,
  1917.            sx, sy + side->ui->fh + 1, 100 + 1, 10 + 1);
  1918. }
  1919.